local _G = GLOBAL
--===============================================================
--[[ 重写小木牌的绘图机制，让小木牌可以画上本mod里的256*256高清贴图 ]]
--===============================================================

-- mod中有物品栏图片的prefabs的表
local invPrefabList = require("sw_minisign_list")
local invBuildMaps = { "sw_minisign1" }
-- 这里image是所用图片的名字，而非prefab的名字
local function OnDrawn_minisign(inst, image, src, atlas, bgimage, bgatlas, ...)
    if inst.drawable_ondrawnfn_sw ~= nil then
        inst.drawable_ondrawnfn_sw(inst, image, src, atlas, bgimage, bgatlas, ...)
    end
    -- src在重载后就没了，所以没法让信息存在src里
    if image ~= nil and invPrefabList[image] ~= nil then
        inst.AnimState:OverrideSymbol("SWAP_SIGN", invBuildMaps[invPrefabList[image]] or invBuildMaps[1], image)
    end
    if bgimage ~= nil and invPrefabList[bgimage] ~= nil then
        inst.AnimState:OverrideSymbol("SWAP_SIGN_BG", invBuildMaps[invPrefabList[bgimage]] or invBuildMaps[1], bgimage)
    end
end
local function MiniSign_init(inst)
    if inst.drawable_ondrawnfn_sw == nil and inst.components.drawable ~= nil then
        inst.drawable_ondrawnfn_sw = inst.components.drawable.ondrawnfn
        inst.components.drawable:SetOnDrawnFn(OnDrawn_minisign)
    end
end
AddPrefabPostInit("minisign", MiniSign_init)
AddPrefabPostInit("minisign_drawn", MiniSign_init)
AddPrefabPostInit("decor_pictureframe", MiniSign_init)

--===============================================================
--[[ 高清256*256贴图兼容棱镜的白木展示柜 ]]
--===============================================================

local function Sw_SetShowSlot(inst, slot)
    local item = inst.components.container.slots[slot]
    if item ~= nil then
        local bgimage
        local image = FunctionOrValue(item.drawimageoverride, item, inst) or (#(item.components.inventoryitem.imagename or "") > 0 and item.components.inventoryitem.imagename) or item.prefab or nil
        if image ~= nil then
            if item.inv_image_bg ~= nil and item.inv_image_bg.image ~= nil and item.inv_image_bg.image:len() > 4 and item.inv_image_bg.image:sub(-4):lower() == ".tex" then
                bgimage = item.inv_image_bg.image:sub(1, -5)
            end
            if invPrefabList[image] ~= nil then
                inst.AnimState:ClearOverrideSymbol("slot" .. tostring(slot))
                inst.AnimState:OverrideSymbol("slot" .. tostring(slot), invBuildMaps[invPrefabList[image]] or invBuildMaps[1], image)
            end
            if bgimage ~= nil then
                if invPrefabList[bgimage] ~= nil then
                    inst.AnimState:ClearOverrideSymbol("slotbg" .. tostring(slot))
                    inst.AnimState:OverrideSymbol("slotbg" .. tostring(slot), invBuildMaps[invPrefabList[bgimage]] or invBuildMaps[1], bgimage)
                end
            end
        end
    end
end
local function Sw_ItemGet_chest(inst, data)
    if data and data.slot and data.slot <= inst.shownum_l then
        Sw_SetShowSlot(inst, data.slot)
    end
end
local function HookWhitewood(inst)
    if not TheWorld.ismastersim then
        return
    end
    local old_onclosefn = inst.components.container.onclosefn
    local function OnClose_chest(inst)
        old_onclosefn(inst)
        if not inst:HasTag("burnt") then
            for i = 1, inst.shownum_l, 1 do
                Sw_SetShowSlot(inst, i)
            end
        end
    end
    inst.components.container.onclosefn = OnClose_chest
    inst:ListenForEvent("itemget", Sw_ItemGet_chest)
    inst:ListenForEvent("itemlose", Sw_ItemGet_chest)
end
if _G.SHADOWWOVEN_SETS.ENABLEDMODS["legion"] then
    AddPrefabPostInit("chest_whitewood", HookWhitewood)
    AddPrefabPostInit("chest_whitewood_inf", HookWhitewood)
    AddPrefabPostInit("chest_whitewood_big", HookWhitewood)
    AddPrefabPostInit("chest_whitewood_big_inf", HookWhitewood)
end
-- 顺手兼容一下食趣的冰柜吧
if _G.SHADOWWOVEN_SETS.ENABLEDMODS["tastefun"] then
    AddPrefabPostInit("tf_beverage_cabinet", HookWhitewood)
end

--===============================================================
--[[ 击败梦魇疯猪和拾荒疯猪掉落晗猪草种子,以及猪机人蓝图 ]]
--===============================================================

local function has_item(tbl, target)
    for _, v in ipairs(tbl) do
        if v[1] == target then
            return true
        end
    end
    return false
end

AddPrefabPostInit("daywalker", function(inst)
    if not TheWorld.ismastersim then
        return
    end
    local LootTables = LootTables
    if LootTables and LootTables.daywalker ~= nil then
        if not has_item(LootTables.daywalker, "sw_piggymimosa_seeds") then
            table.insert(LootTables.daywalker, { "sw_piggymimosa_seeds", 1 })
            table.insert(LootTables.daywalker, { "sw_piggymimosa_seeds", 0.5 })
            table.insert(LootTables.daywalker, { "sw_piggymimosa_seeds", 0.5 })
            table.insert(LootTables.daywalker, { "sw_piggymimosa_seeds", 0.5 })
            table.insert(LootTables.daywalker, { "sw_piggymimosa_seeds", 0.5 })
        end
    end
end)

AddPrefabPostInit("daywalker2", function(inst)
    if not TheWorld.ismastersim then
        return
    end
    local LootTables = LootTables
    if LootTables and LootTables.daywalker2 ~= nil then
        if not has_item(LootTables.daywalker2, "sw_piggymimosa_seeds") then
            table.insert(LootTables.daywalker2, { "sw_piggymimosa_seeds", 1 })
            table.insert(LootTables.daywalker2, { "sw_piggymimosa_seeds", 0.5 })
            table.insert(LootTables.daywalker2, { "sw_piggymimosa_seeds", 0.5 })
            table.insert(LootTables.daywalker2, { "sw_piggymimosa_seeds", 0.5 })
            table.insert(LootTables.daywalker2, { "sw_piggymimosa_seeds", 0.5 })
            table.insert(LootTables.daywalker2, { "sw_pigbot_blueprint", 1 })
        end
    end
end)

--===============================================================
--[[ 夜莓小蛋糕相关,代码套用了能力勋章模组,复眼勋章的写法,大爱恒子 ]]
--===============================================================

-- 鲜艳滤镜(原画)
local NIGHT_BERRY_CAKE_COLOURCUBES = {
    day = "images/colour_cubes/identity_colourcube.tex",
    dusk = "images/colour_cubes/identity_colourcube.tex",
    night = "images/colour_cubes/identity_colourcube.tex",
    full_moon = "images/colour_cubes/identity_colourcube.tex",
}

-- 夜莓小蛋糕食用后执行函数
local function SetNightBerryCakeVision(inst)
    if inst.components.playervision then
        if inst.swnightvision and inst.swnightvision:value() then
            inst.components.playervision:ForceGoggleVision(true)
            inst.components.playervision:PushForcedNightVision("sw_night_berry_cake", 10, NIGHT_BERRY_CAKE_COLOURCUBES, true)
        else
            inst.components.playervision:ForceGoggleVision(false)
            inst.components.playervision:PopForcedNightVision("sw_night_berry_cake")
        end
    end
end

-- 玩家初始化
AddPlayerPostInit(function(inst)
    -- 夜莓小蛋糕
    inst.swnightvision = GLOBAL.net_bool(inst.GUID, "swnightvision", "swnightvisiondirty")
    inst:ListenForEvent("swnightvisiondirty", SetNightBerryCakeVision)

    -- 吃了夜莓小蛋糕,玩家处于光亮中
    local oldIsInLight = inst.IsInLight
    if oldIsInLight ~= nil then
        inst.IsInLight = function(inst, ...)
            if inst.swnightvision and inst.swnightvision:value() then
                return true
            end
            return oldIsInLight(inst, ...)
        end
    end

    -- 服务器端
    if TheWorld.ismastersim then
    end
end)

-- 隐藏沙漠护目镜底部周围的滤镜
AddClassPostConstruct("widgets/gogglesover", function(self)
    local oldToggleGoggles = self.ToggleGoggles
    self.ToggleGoggles = function(self, show, ...)
        if self.owner ~= nil and self.owner.swnightvision and self.owner.swnightvision:value() then
            show = false
        end
        if oldToggleGoggles then
            oldToggleGoggles(self, show, ...)
        end
    end
end)

-- 防神话刷帧
AddComponentPostInit("playervision", function(self)
    local oldForceGoggleVision = self.ForceGoggleVision
    if oldForceGoggleVision then
        self.ForceGoggleVision = function(self, force, ...)
            local isequipped = self.inst and self.inst.swnightvision and self.inst.swnightvision:value()
            force = force or isequipped
            if oldForceGoggleVision then
                oldForceGoggleVision(self, force, ...)
            end
        end
    end
    local oldForceNightVision = self.ForceNightVision
    if oldForceNightVision then
        self.ForceNightVision = function(self, force, ...)
            local isequipped = self.inst and self.inst.swnightvision and self.inst.swnightvision:value()
            force = force or isequipped
            if oldForceNightVision then
                oldForceNightVision(self, force, ...)
            end
        end
    end
end)

--===============================================================
--[[ 靠近暗影之冕装备特效,制作栏显示远古科技栏 ]]
--===============================================================

AddPrototyperDef("sw_shadow_crown_equipped", { -- 第一个参数是指玩家靠近时会解锁科技的prefab名
    icon_atlas = "images/crafting_menu_icons.xml",
    icon_image = "station_crafting_table.tex",
    is_crafting_station = true,
    filter_text = STRINGS.UI.CRAFTING_STATION_FILTERS.ANCIENT
})

--===============================================================
--[[ 击败远古织影者掉落暗影之冕 ]]
--===============================================================

AddPrefabPostInit("stalker_atrium", function(inst)
    if not TheWorld.ismastersim then
        return
    end
    local lootdropper = inst.components.lootdropper
    if lootdropper then
        local old_lootsetupfn = lootdropper.lootsetupfn
        local function AtriumLootFn(lootdropper1, ...)
            old_lootsetupfn(lootdropper1, ...)
            if lootdropper1.inst.atriumdecay then
            else
                lootdropper1:AddChanceLoot("sw_shadow_crown", 1) -- 掉落暗影之冕
            end
        end
        lootdropper:SetLootSetupFn(AtriumLootFn)
    end
end)

--===============================================================
--[[ 火腿棒和警告表添加升级组件 ]]
--===============================================================

local prefabs = {
    "pocketwatch_weapon",
    "hambat",
    "abigail_flower",
    "abigail",
    "shieldofterror",
    "siestahut",
    "tent",
}
if _G.SHADOWWOVEN_SETS.ENABLEDMODS["musha"] then
    table.insert(prefabs, "mushasword_frost")
    table.insert(prefabs, "phoenixspear")
    table.insert(prefabs, "mushasword")
    table.insert(prefabs, "mushasword4")
    table.insert(prefabs, "frosthammer")
end
for _, prefab in ipairs(prefabs) do
    AddPrefabPostInit(prefab, function(inst)
        inst:AddTag("showswinfo")
        if TheWorld.ismastersim then
            inst:AddComponent("sw_upgrade")
        end
        inst.getSwInfo = function(inst)
            local swstr = ""
            if not STRINGS.SW_STRINGS then
                return swstr
            end
            if inst.components.sw_upgrade.upgraded_type == nil then
                swstr = STRINGS.SW_STRINGS.SW_UPGRADE_INFO and STRINGS.SW_STRINGS.SW_UPGRADE_INFO.NONE or ""
            elseif inst.components.sw_upgrade.upgraded_type == "enlight" then
                swstr = STRINGS.SW_STRINGS.SW_UPGRADE_INFO and STRINGS.SW_STRINGS.SW_UPGRADE_INFO.ENLIGHT or ""
            elseif inst.components.sw_upgrade.upgraded_type == "shadow" then
                swstr = STRINGS.SW_STRINGS.SW_UPGRADE_INFO and STRINGS.SW_STRINGS.SW_UPGRADE_INFO.SHADOW or ""
            elseif inst.components.sw_upgrade.upgraded_type == "tent" then
                swstr = STRINGS.SW_STRINGS.SW_UPGRADE_INFO and STRINGS.SW_STRINGS.SW_UPGRADE_INFO.TENT or ""
            end
            return swstr
        end
    end)
end

--===============================================================
--[[ 阿比盖尔之花召唤出的阿比具有位面伤害和阵营加伤 ]]
--===============================================================

local function SwHookGhostlyBond(self)
    local OldSummon = self.Summon
    self.Summon = function(self, summoningitem, pos, ...)
        local success = OldSummon(self, summoningitem, pos, ...)
        if success == true then
            local sw_upgrade = summoningitem.components.sw_upgrade
            local pd = self.ghost.components.planardamage
            local bonus = self.ghost.components.damagetypebonus
            if sw_upgrade and pd and bonus then
                if sw_upgrade.upgraded_type == "shadow" and self.ghost.components.sw_upgrade.upgraded_type == nil then
                    pd:SetBaseDamage(pd:GetBaseDamage() + 20)
                    bonus:AddBonus("lunar_aligned", self.ghost, 1.1, "sw_upgrade")
                    self.ghost.components.sw_upgrade:DoUpgrade("shadow")
                elseif sw_upgrade.upgraded_type == "shadow" and self.ghost.components.sw_upgrade.upgraded_type == "enlight" then
                    bonus:RemoveBonus("shadow_aligned", self.ghost, "sw_upgrade")
                    bonus:AddBonus("lunar_aligned", self.ghost, 1.1, "sw_upgrade")
                    self.ghost.components.sw_upgrade.upgraded_type = nil
                    self.ghost.components.sw_upgrade:DoUpgrade("shadow")
                elseif sw_upgrade.upgraded_type == "enlight" and self.ghost.components.sw_upgrade.upgraded_type == nil then
                    pd:SetBaseDamage(pd:GetBaseDamage() + 20)
                    bonus:AddBonus("shadow_aligned", self.ghost, 1.1, "sw_upgrade")
                    self.ghost.components.sw_upgrade:DoUpgrade("enlight")
                elseif sw_upgrade.upgraded_type == "enlight" and self.ghost.components.sw_upgrade.upgraded_type == "shadow" then
                    bonus:RemoveBonus("lunar_aligned", self.ghost, "sw_upgrade")
                    bonus:AddBonus("shadow_aligned", self.ghost, 1.1, "sw_upgrade")
                    self.ghost.components.sw_upgrade.upgraded_type = nil
                    self.ghost.components.sw_upgrade:DoUpgrade("enlight")
                elseif sw_upgrade.upgraded_type == nil and self.ghost.components.sw_upgrade.upgraded_type ~= nil then
                    pd:SetBaseDamage(pd:GetBaseDamage() - 20)
                    if self.ghost.components.sw_upgrade.upgraded_type == "shadow" then
                        bonus:RemoveBonus("lunar_aligned", self.ghost, "sw_upgrade")
                    elseif self.ghost.components.sw_upgrade.upgraded_type == "enlight" then
                        bonus:RemoveBonus("shadow_aligned", self.ghost, "sw_upgrade")
                    end
                    self.ghost.components.sw_upgrade.upgraded_type = nil
                end
            end
            return true
        else
            return false
        end
    end
end
AddComponentPostInit("ghostlybond", SwHookGhostlyBond)

--===============================================================
--[[ 小月灵不攻击被光影交织塔保护的玩家 ]]
--===============================================================

AddPrefabPostInit("gestalt", function(inst)
    if TheWorld.ismastersim then
        local old_targetfn = inst.components.combat.targetfn
        local new_targetfn = function(inst)
            if inst.tracking_target and inst.tracking_target.sw_shadowwoven_tower then
                return nil
            end
            return old_targetfn(inst)
        end
        inst.components.combat:SetRetargetFunction(1, new_targetfn)
    end
end)

--===============================================================
--[[ 猪器人专属燃料 ]]
--===============================================================

FUELTYPE.SW_PIGBOT = "SW_PIGBOT"

local item_fuel =
{
    sw_piggymimosa = 1920, -- 晗猪草
}

for k, v in pairs(item_fuel) do
    AddPrefabPostInit(k, function(inst)
        if inst.components.fuel == nil then
            inst:AddComponent("fuel")
        end
        inst.components.fuel.fueltype = FUELTYPE.SW_PIGBOT
        inst.components.fuel.fuelvalue = v
    end)
end

--===============================================================
--[[ 可以给予月亮石琉璃树苗,月圆升级成为月光琉璃树苗 ]]
--===============================================================

local function Has(inst, staffname)
    return (inst._staffinst ~= nil and inst._staffinst.prefab or inst.components.pickable.product) == staffname
end

local function IsFixed(inst)
    return inst.components.workable.workleft > TUNING.MOONBASE_DAMAGED_WORK
end

local function GetAnimState(inst)
    return (IsFixed(inst) and "full")
        or (inst.components.workable.workleft * 2 > TUNING.MOONBASE_DAMAGED_WORK and "med")
        or (inst.components.workable.workleft > 0 and "medlow")
        or "low"
end

local function StartFX(inst)
    if inst._fxfront == nil or inst._fxback == nil then
        local x, y, z = inst.Transform:GetWorldPosition()

        if inst._fxpulse ~= nil then
            inst._fxpulse:Remove()
        end
        inst._fxpulse = SpawnPrefab("positronpulse")
        inst._fxpulse.Transform:SetPosition(x, y, z)

        if inst._fxfront ~= nil then
            inst._fxfront:Remove()
        end
        inst._fxfront = SpawnPrefab("positronbeam_front")
        inst._fxfront.Transform:SetPosition(x, y, z)

        if inst._fxback ~= nil then
            inst._fxback:Remove()
        end
        inst._fxback = SpawnPrefab("positronbeam_back")
        inst._fxback.Transform:SetPosition(x, y, z)

        if inst._startlighttask ~= nil then
            inst._startlighttask:Cancel()
        end
        inst._startlighttask = inst:DoTaskInTime(3 * FRAMES, StartLight)
    end
    if inst._stoplighttask ~= nil then
        inst._stoplighttask:Cancel()
        inst._stoplighttask = nil
    end
end

local function StopLight(inst)
    inst._stoplighttask = nil
    inst.Light:Enable(false)
    if inst._staffstar == nil then
        inst.AnimState:ClearBloomEffectHandle()
    end
end

local function StopFX(inst)
    if inst._fxpulse ~= nil then
        inst._fxpulse:KillFX()
        inst._fxpulse = nil
    end
    if inst._fxfront ~= nil or inst._fxback ~= nil then
        if inst._fxback ~= nil then
            inst._fxfront:KillFX()
            inst._fxfront = nil
        end
        if inst._fxback ~= nil then
            inst._fxback:KillFX()
            inst._fxback = nil
        end
        if inst._stoplighttask ~= nil then
            inst._stoplighttask:Cancel()
        end
        inst._stoplighttask = inst:DoTaskInTime(9 * FRAMES, StopLight)
    end
    if inst._startlighttask ~= nil then
        inst._startlighttask:Cancel()
        inst._startlighttask = nil
    end
end

local function PushMusic(inst, level)
    if ThePlayer ~= nil and ThePlayer:IsNear(inst, 30) then
        ThePlayer:PushEvent("triggeredevent", { name = "moonbase", level = level })
    end
end

local function OnMusicDirty(inst)
    -- Dedicated server does not need to trigger music
    if not TheNet:IsDedicated() then
        if inst._musictask ~= nil then
            inst._musictask:Cancel()
        end
        inst._musictask = inst._music:value() > 0 and inst:DoPeriodicTask(1, PushMusic, 0, inst._music:value()) or nil
    end
end

local function StartMusic(inst, level)
    if inst._music:value() ~= level then
        inst._music:set(level)
        OnMusicDirty(inst)
    end
end

local function Sw_ToggleMoonCharge(inst)
    if inst.components.workable.workleft <= 0 then
        inst.components.trader:Disable()

        if inst.components.lootdropper == nil then
            inst:AddComponent("lootdropper")

            for i = 1, math.random(2) do
                inst.components.lootdropper:SpawnLootPrefab("moonrocknugget")
            end
            inst.SoundEmitter:PlaySound("dontstarve/wilson/rock_break")
        end

        if inst.components.pickable.caninteractwith then
            inst.components.pickable.caninteractwith = false

            inst.AnimState:ClearOverrideSymbol("swap_staffs")

            if inst._staffstar ~= nil then
                inst._staffstar:Remove()
                inst._staffstar = nil
                if not inst.Light:IsEnabled() then
                    inst.AnimState:ClearBloomEffectHandle()
                end
            end

            if inst._staffinst ~= nil then
                inst:RemoveChild(inst._staffinst)
                inst._staffinst:ReturnToScene()
                inst._staffinst.components.inventoryitem:InheritWorldWetnessAtTarget(inst)
                inst.components.lootdropper:FlingItem(inst._staffinst)
                inst._staffinst = nil
                inst._staffuse = nil
            else
                local staff = inst.components.lootdropper:SpawnLootPrefab(inst.components.pickable.product)
                if inst._staffuse ~= nil then
                    if staff ~= nil and staff.components.finiteuses ~= nil then
                        staff.components.finiteuses:SetUses(inst._staffuse)
                    end
                    inst._staffuse = nil
                end
            end
        end

        inst.components.timer:StopTimer("sw_moonchargepre")
        inst.components.timer:StopTimer("sw_mooncharge")
        inst.components.timer:StopTimer("sw_mooncharge2")
        inst.components.timer:StopTimer("sw_mooncharge3")
        inst:RemoveTag("intense")
        inst.components.moonbeastspawner:Stop()
        local state = GetAnimState(inst)
        if state ~= "low" then
            if inst.AnimState:IsCurrentAnimation("recharging_" .. state) then
                inst.AnimState:PlayAnimation(state)
            else
                inst.AnimState:PushAnimation(state, false)
            end
        end
        StopFX(inst)
    else
        if inst.components.lootdropper ~= nil then
            inst:RemoveComponent("lootdropper")
        end

        if TheWorld.state.isfullmoon and
            inst.components.pickable.caninteractwith and
            Has(inst, "sw_glass_tree_sapling_item") then
            -- Start moon charging
            local state = GetAnimState(inst)
            if state ~= "low" then
                if inst.AnimState:IsCurrentAnimation(state) then
                    inst.AnimState:PlayAnimation("recharging_" .. state, true)
                else
                    inst.AnimState:PushAnimation("recharging_" .. state, true)
                end
            end
            if inst.components.timer:TimerExists("sw_mooncharge") then
                inst.components.moonbeastspawner:Start()
                StartFX(inst)
                StartMusic(inst, (inst.components.timer:TimerExists("sw_mooncharge2") or inst.components.timer:TimerExists("sw_mooncharge3")) and 1 or 2)
            elseif not inst.components.timer:TimerExists("sw_moonchargepre") then
                inst.components.timer:StartTimer("sw_moonchargepre", math.max(1, inst.components.timer:GetTimeLeft("sw_fullmoonstartdelay") or 1))
                inst.components.timer:StopTimer("sw_fullmoonstartdelay")
            end
        else
            if not inst.components.pickable.caninteractwith then
                if IsFixed(inst) then
                    inst.components.trader:Enable()
                else
                    inst.components.trader:Disable()
                end
            end

            --Stop moon charging
            inst.components.timer:StopTimer("sw_moonchargepre")
            inst.components.timer:StopTimer("sw_mooncharge")
            inst.components.timer:StopTimer("sw_mooncharge2")
            inst.components.timer:StopTimer("sw_mooncharge3")
            inst:RemoveTag("intense")
            inst.components.moonbeastspawner:Stop()
            local state = GetAnimState(inst)
            if state ~= "low" then
                if inst.AnimState:IsCurrentAnimation("recharging_" .. state) then
                    inst.AnimState:PlayAnimation(state)
                else
                    inst.AnimState:PushAnimation(state, false)
                end
            end
            StopFX(inst)
        end
    end
end

local function Sw_OnTimerDone(inst, data)
    if data.name == "sw_moonchargepre" then
        if inst.components.timer:TimerExists("sw_fullmoonstartdelay") then
            inst.components.timer:StartTimer("sw_moonchargepre", inst.components.timer:GetTimeLeft("sw_fullmoonstartdelay"))
            inst.components.timer:StopTimer("sw_fullmoonstartdelay")
        else
            inst.components.timer:StartTimer("sw_mooncharge", TUNING.MOONBASE_CHARGE_DURATION)
            inst.components.timer:StartTimer("sw_mooncharge2", math.min(TUNING.MOONBASE_CHARGE_DURATION1, TUNING.MOONBASE_CHARGE_DURATION))
            inst.components.moonbeastspawner:Start()
            StartFX(inst)
            StartMusic(inst, 1)
        end
    elseif data.name == "sw_mooncharge2" then
        if inst._fxpulse ~= nil then
            inst._fxpulse:SetLevel(2)
        end
        inst:AddTag("intense")
        inst.components.timer:StartTimer("sw_mooncharge3", math.min(TUNING.MOONBASE_CHARGE_DURATION1, math.max(0, TUNING.MOONBASE_CHARGE_DURATION - TUNING.MOONBASE_CHARGE_DURATION1)))
    elseif data.name == "sw_mooncharge3" then
        if inst._fxpulse ~= nil then
            inst._fxpulse:SetLevel(3)
        end
        StartMusic(inst, 2)
    elseif data.name == "sw_mooncharge"
        and inst.components.pickable.caninteractwith
        and Has(inst, "sw_glass_tree_sapling_item") then
        inst.components.pickable:ChangeProduct("sw_moonlight_glass_tree_sapling_item")

        inst.AnimState:OverrideSymbol("swap_staffs", "sw_moonlight_glass_tree", "trunk")

        inst._staffuse = nil

        if inst._fxpulse ~= nil then
            inst._fxpulse:FinishFX()
            inst._fxpulse = nil
        end

        if not inst.sw_loading then
            inst.components.moonbeastspawner:ForcePetrify()
            StartMusic(inst, 0)
            Sw_ToggleMoonCharge(inst)
        end
    end
end

local function Sw_OnFullmoon(inst, isfullmoon)
    if not isfullmoon then
        inst.components.timer:StopTimer("sw_fullmoonstartdelay")
        StartMusic(inst, 0)
    elseif not inst.components.timer:TimerExists("sw_fullmoonstartdelay") then
        inst.components.timer:StartTimer("sw_fullmoonstartdelay", TUNING.MOONBASE_CHARGE_DELAY)
    end
    Sw_ToggleMoonCharge(inst)
end

local function sw_init(inst)
    inst.sw_loading = nil
    inst:WatchWorldState("isfullmoon", Sw_OnFullmoon)
    Sw_ToggleMoonCharge(inst)
end

AddPrefabPostInit("moonbase", function(inst)
    if not TheWorld.ismastersim then
        return
    end

    local old_abletoaccepttest = inst.components.trader.abletoaccepttest
    local function New_ItemTradeTest(inst, item, ...)
        if item.prefab == "sw_glass_tree_sapling_item" then
            return true
        elseif item.prefab == "sw_moonlight_glass_tree_sapling_item" then
            return true
        end
        return old_abletoaccepttest(inst, item, ...)
    end
    inst.components.trader:SetAbleToAcceptTest(New_ItemTradeTest)

    local old_onaccept = inst.components.trader.onaccept
    local function New_OnStaffGiven(inst, giver, item, ...)
        if old_onaccept then
            old_onaccept(inst, giver, item, ...)
        else
            return
        end
        local item_name
        if type(item) == "table" then
            item_name = item.prefab
        else
            item_name = item
        end
        if item_name == "sw_glass_tree_sapling_item" then
            inst.components.pickable:SetUp(item_name, 1000000)
            inst.AnimState:OverrideSymbol("swap_staffs", "sw_glass_tree_sapling", "trunk")
        elseif item_name == "sw_moonlight_glass_tree_sapling_item" then
            inst.components.pickable:SetUp(item_name, 1000000)
            inst.AnimState:OverrideSymbol("swap_staffs", "sw_moonlight_glass_tree", "trunk")
        end
        if not inst.sw_loading then
            Sw_ToggleMoonCharge(inst)
        end
    end
    inst.components.trader.onaccept = New_OnStaffGiven

    local old_onwork = inst.components.workable.onwork
    local function New_UpdateWorkState(inst)
        old_onwork(inst)
        if not inst.sw_loading then
            Sw_ToggleMoonCharge(inst)
        end
    end
    inst.components.workable:SetOnWorkCallback(New_UpdateWorkState)
    inst.components.workable:SetOnLoadFn(New_UpdateWorkState)

    inst:ListenForEvent("timerdone", Sw_OnTimerDone)

    local old_onload = inst.OnLoad
    inst.OnLoad = function(inst, data)
        if old_onload then
            old_onload(inst, data)
        end
        if inst.components.pickable.caninteractwith and data.staffname ~= nil then
            New_OnStaffGiven(inst, nil, data.staffname)
        end
    end

    inst.sw_loading = inst:DoTaskInTime(0, sw_init)
end)

--===============================================================
--[[ 修好远古大门,查理离开留下更深暗影石 ]]
--===============================================================

AddPrefabPostInit("charlie_npc", function(inst)
    if not TheWorld.ismastersim then
        return
    end
    local old_Despawn = inst.Despawn
    inst.Despawn = function(inst)
        local x, y, z = inst.Transform:GetWorldPosition()
        if old_Despawn then
            old_Despawn(inst)
        end
        local despawn_time = (60 + 60 + 100) * FRAMES
        inst.spawn_task = inst:DoTaskInTime(despawn_time, function()
            local sw_deeper_shadowstone = SpawnPrefab("sw_deeper_shadowstone")
            if sw_deeper_shadowstone ~= nil then
                sw_deeper_shadowstone.Transform:SetPosition(x, y, z)
            end

            local fx = SpawnPrefab("cavein_dust_low")
            if fx ~= nil then
                fx.Transform:SetPosition(x, y, z)
            end
        end)
    end
end)

--===============================================================
--[[ 开启的地洞入口和地洞楼梯,赋予镇守组件 ]]
--===============================================================

AddPrefabPostInit("cave_entrance_open", function(inst)
    inst:AddTag("showswinfo")
    if TheWorld.ismastersim then
        inst:AddComponent("sw_cordon")
        local oldcanspawn = inst.components.childspawner.canspawnfn
        inst.components.childspawner.canspawnfn = function(inst, ...)
            if inst.components.sw_cordon.cordon then
                return false
            end
            return oldcanspawn(inst, ...)
        end
    end
    inst.getSwInfo = function(inst)
        local swstr = inst.components.sw_cordon.cordon == true and "镇守" or "未镇守"
        return swstr
    end
end)

AddPrefabPostInit("cave_exit", function(inst)
    inst:AddTag("showswinfo")
    if TheWorld.ismastersim then
        inst:AddComponent("sw_cordon")
    end
    inst.getSwInfo = function(inst)
        local swstr = inst.components.sw_cordon.cordon == true and "镇守" or "未镇守"
        return swstr
    end
end)

--===============================================================
--[[ 给世界赋予失衡组件 ]]
--===============================================================

AddPrefabPostInit("world", function(inst)
    if not TheWorld.ismastersim then
        return
    end
    inst:AddComponent("sw_sealsinkholemanager")
end)

--===============================================================
--[[ 世界失衡状态下,玩家所受精神光环影响 ]]
--===============================================================

AddPlayerPostInit(function(inst)
    if not TheWorld.ismastersim then
        return
    end

    -------------------------------------------------------------------
    -- 定义光环强度（每秒）
    -- 饥荒一天 480 秒
    -- 每分钟 60 秒
    -- 每分钟 +5 理智 ⇒ 每秒约 +5/60 = 0.0833
    -- 每分钟 +10 理智 ⇒ 每秒约 +10/60 = 0.1666
    -------------------------------------------------------------------
    local AURA_UNBALANCE = 5 / 60
    local AURA_STRONG    = 10 / 60

    -- 逻辑函数：更新光环效果
    local function UpdateSanityAura(_, data)
        if not inst.components.sanity then
            return
        end

        local sanity = inst.components.sanity.externalmodifiers
        local world = TheWorld
        if not world then
            return
        end
        local cave = world:HasTag("cave")

        -- 移除旧的光环
        sanity:RemoveModifier("sealsinkholemanager", "seal_unbalance_aura")
        inst.components.sanity:EnableLunacy(false, "world_unbalance")
        inst.components.sanity:SetInducedLunacy(world, false)

        -- 如果世界进入失衡状态
        if world.components.sw_sealsinkholemanager ~= nil then
            if cave then
                if world.components.sw_sealsinkholemanager.is_strong_unbalanced then
                    sanity:SetModifier("sealsinkholemanager", -AURA_STRONG, "seal_unbalance_aura")
                elseif world.components.sw_sealsinkholemanager.is_unbalanced then
                    sanity:SetModifier("sealsinkholemanager", -AURA_UNBALANCE, "seal_unbalance_aura")
                end
            else
                if world.components.sw_sealsinkholemanager.is_strong_unbalanced then
                    inst.components.sanity:EnableLunacy(true, "world_unbalance")
                    sanity:SetModifier("sealsinkholemanager", AURA_STRONG, "seal_unbalance_aura")
                elseif world.components.sw_sealsinkholemanager.is_unbalanced then
                    inst.components.sanity:EnableLunacy(true, "world_unbalance")
                    sanity:SetModifier("sealsinkholemanager", AURA_UNBALANCE, "seal_unbalance_aura")
                end
            end
        end
    end

    -- 玩家进入游戏时同步一次
    inst:DoTaskInTime(1, function()
        UpdateSanityAura()
    end)

    -- 监听世界事件
    inst:ListenForEvent("seal_unbalanced_changed", UpdateSanityAura, TheWorld)
    inst:ListenForEvent("seal_strong_unbalanced_changed", UpdateSanityAura, TheWorld)
end)

--===============================================================
--[[ 添加玩家本地变量 ]]
--===============================================================
AddPrefabPostInit("player_classified", function(inst)
    -- 添加物品详细信息
    inst.sw_info = "" -- 详细信息
    inst.net_sw_info = _G.net_string(inst.GUID, "sw_info", "sw_infodirty")
    if TheNet:GetIsClient() or (TheNet:GetIsServer() and not TheNet:IsDedicated()) then
        -- 添加物品详细信息
        inst:ListenForEvent("sw_infodirty", function(inst)
            inst.sw_info = inst.net_sw_info:value()
        end)
    end
end)


local function CheckUserHint(inst)
    local classified = ThePlayer and ThePlayer.player_classified
    if classified == nil then
        return ""
    end
    local i = string.find(classified.sw_info, ';', 1, true)
    if i == nil then
        return ""
    end
    local guid = tonumber(classified.sw_info:sub(1, i - 1))
    if guid ~= inst.GUID then
        return ""
    end
    return classified.sw_info:sub(i + 1)
end
local save_target         -- 暂存目标
local last_check_time = 0 -- 上一次检查时间
AddClassPostConstruct("widgets/hoverer", function(hoverer)
    local oldSetString = hoverer.text.SetString
    hoverer.text.SetString = function(text, str, ...)
        -- 获取目标
        local target = GLOBAL.TheInput:GetHUDEntityUnderMouse()
        target = (target and target.widget and target.widget.parent ~= nil and target.widget.parent.item) or TheInput:GetWorldEntityUnderMouse() or nil
        -- 获取需显示信息
        if target and target.GUID and target:HasTag("showswinfo") then
            local str2 = CheckUserHint(target)
            if str2 and str2 ~= "" then
                str = str .. str2
            end
            -- 目标变了或者时间间隔1秒，则发送rpc
            if target ~= save_target or last_check_time + 1 < GetTime() then
                save_target = target
                last_check_time = GetTime()
                SendModRPCToServer(MOD_RPC.SHADOWWOVEN.Showinfo, save_target.GUID, save_target)
            end
        end

        if oldSetString then
            return oldSetString(text, str, ...)
        end
    end
end)

--===============================================================
--[[ 洞穴失衡时,精神值低于50%就进入疯狂状态 ]]
--===============================================================

local function SwHookSanity(self)
    local OldIsCrazy = self.IsCrazy
    self.IsCrazy = function(self, ...)
        if TheWorld and TheWorld:HasTag("cave") and TheWorld.components.sw_sealsinkholemanager.is_unbalanced then
            if self.current / self.max < 0.5 then
                return true
            end
        end
        return OldIsCrazy(self, ...)
    end
end
AddComponentPostInit("sanity", SwHookSanity)

--===============================================================
--[[ 本mod远程减伤害逻辑 ]]
--===============================================================

local function SwHookHealth(self)
    self.sw_RangedDamageReduction = false

    local OldDoDelta = self.DoDelta
    self.DoDelta = function(self, amount, overtime, cause, ignore_invincible, afflicter, ignore_absorb, ...)
        if afflicter == nil or amount > 0 then
            return OldDoDelta(self, amount, overtime, cause, ignore_invincible, afflicter, ignore_absorb, ...)
        end
        if afflicter:IsValid() and self.inst:IsValid() then
            local distance = math.sqrt(self.inst:GetDistanceSqToInst(afflicter)) -- 计算距离
            if distance > 1.1 and self.sw_RangedDamageReduction then
                amount = amount * 0.5
            end
        end
        return OldDoDelta(self, amount, overtime, cause, ignore_invincible, afflicter, ignore_absorb, ...)
    end
end
AddComponentPostInit("health", SwHookHealth)

--===============================================================
--[[ 世界强失衡时,这些生物的初始生命值将是原先的两倍 ]]
--===============================================================

local PREFABS =
{
    "alterguardian_phase1",
    "alterguardian_phase2",
    "alterguardian_phase3",
    "lunarthrall_plant",
    "mutatedbearger",
    "mutatedwarg",
    "mutateddeerclops",

    "stalker_atrium",
    "daywalker",
    "shadowthrall_wings",
    "shadowthrall_hands",
    "shadowthrall_horns",
    "shadowthrall_mouth",
}
for _, prefab in ipairs(PREFABS) do
    AddPrefabPostInit(prefab, function(inst)
        if TheWorld.ismastersim then
            local is_strong_unbalanced = TheWorld.components.sw_sealsinkholemanager.is_strong_unbalanced
            if is_strong_unbalanced then
                if inst.components.health then
                    inst.components.health.maxhealth = inst.components.health.maxhealth * 2
                    inst.components.health:SetCurrentHealth(inst.components.health.currenthealth * 2)
                    inst.components.health.sw_RangedDamageReduction = true
                end
            end
        end
    end)
end

--===============================================================
--[[ 世界强失衡时,岩石和石笋有额外产出 ]]
--===============================================================

-- 岩石
local rock_flintless = {
    "rock_flintless",
    "rock_flintless_med",
    "rock_flintless_low"
}
for _, prefab in ipairs(rock_flintless) do
    AddPrefabPostInit(prefab, function(inst)
        if not TheWorld.ismastersim then
            return
        end
        local OldOnWork = inst.components.workable.onwork
        local OnWork = function(inst, worker, workleft, ...)
            if workleft <= 0 and TheWorld.components.sw_sealsinkholemanager.is_strong_unbalanced and (not TheWorld:HasTag("cave")) then
                inst.components.lootdropper:SpawnLootPrefab("purebrilliance")
            end
            OldOnWork(inst, worker, workleft, ...)
        end
        inst.components.workable.onwork = OnWork
    end)
end

-- 石笋
local stalagmite = {
    "stalagmite_full",
    "stalagmite_med",
    "stalagmite_low",
    "stalagmite",
    "stalagmite_tall_full",
    "stalagmite_tall_med",
    "stalagmite_tall_low",
    "stalagmite_tall",
}
for _, prefab in ipairs(stalagmite) do
    AddPrefabPostInit(prefab, function(inst)
        if not TheWorld.ismastersim then
            return
        end
        local OldOnWork = inst.components.workable.onwork
        local OnWork = function(inst, worker, workleft, ...)
            if workleft <= 0 and TheWorld.components.sw_sealsinkholemanager.is_strong_unbalanced and TheWorld:HasTag("cave") then
                inst.components.lootdropper:SpawnLootPrefab("horrorfuel")
            end
            OldOnWork(inst, worker, workleft, ...)
        end
        inst.components.workable.onwork = OnWork
    end)
end
